﻿using System;
using System.Net;
using System.IO;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

namespace Handler
{
    /// <summary>
    /// Summary description for Default
    /// </summary>
    public class Default : IHttpHandler
    {

        const string Domain = "127.0.0.1";
        public int MaxPort = 503;
        public int InitialPort = 444;
        const int MaxRetryCount = 10;

        public int RetryCount = 0;
        public string Port = "";
        public string SecurePort = "";
        public string InsecurePort = "";
        public Dictionary<string, string> Ports = new Dictionary<string, string>();
        public string ServerName = "";
        public Queue<int> ports = new Queue<int>();
        public List<Thread> threads = new List<Thread>();
        public bool ThreadsLive = false;
        public bool Secured = true;
        public string Protocol = "https://";

        public void ProcessRequest(HttpContext context)
        {
            ServicePointManager.UseNagleAlgorithm = true;
            System.Net.Security.RemoteCertificateValidationCallback VerifyCertCallback = VerifyCert;
            ServicePointManager.ServerCertificateValidationCallback = VerifyCertCallback;
            ServicePointManager.Expect100Continue = false;
            Secured = context.Request.IsSecureConnection;
            Protocol = Secured ? "https://" : "http://";
            InitialPort = Secured ? 444 : 81;
            Port = Secured ? SecurePort : InsecurePort;
            MaxPort = InitialPort + 60;
            ServerName = context.Request.ServerVariables["SERVER_NAME"];
            SearchPort();
            DoRequest(context);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        public void DoRequest(HttpContext context)
        {
            byte[] buffer = new byte[4096];
            if (Port.Length == 0)
            {
                context.Response.ClearContent();
                context.Response.Write("503 Gateway timeout");
                return;
            }
            ServerName = context.Request.ServerVariables["SERVER_NAME"];
            HttpWebRequest myHttp = (HttpWebRequest)HttpWebRequest.Create(Protocol + Domain + ":" + Port + context.Request.RawUrl.ToString());
            myHttp.AllowAutoRedirect = false;
            myHttp.KeepAlive = true;
            myHttp.UserAgent = context.Request.UserAgent;
            myHttp.Timeout = Int32.MaxValue;
            if (context.Request.Headers["Host"] != null)
            {
                myHttp.Host = context.Request.Headers["Host"];
            }
            if (context.Request.Headers["If-Modified-Since"] != null)
            {
                myHttp.IfModifiedSince = DateTime.Parse(context.Request.Headers["If-Modified-Since"]);
            }
            if (context.Request.UrlReferrer != null)
            {
                myHttp.Referer = context.Request.UrlReferrer.AbsoluteUri;
            }
            foreach (string Header in context.Request.Headers)
            {
                try
                {
                    var Value = context.Request.Headers[Header];
                    myHttp.Headers.Add(Header + ": " + Value);
                }
                catch { }
            }
            myHttp.ContentType = context.Request.ContentType;
            if (context.Request.HttpMethod == "POST")
            {
                myHttp.Method = "POST";
                myHttp.AllowWriteStreamBuffering = true;
                myHttp.ContentLength = context.Request.InputStream.Length;
                Stream requestStream = myHttp.GetRequestStream();
                int length = 0;
                while ((length = context.Request.InputStream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    requestStream.Write(buffer, 0, length);
                }
            }
            try
            {
                HttpWebResponse response = (HttpWebResponse)myHttp.GetResponse();
                if (response.Headers["Location"] != null)
                {
                    context.Response.Redirect(response.Headers["Location"]);
                }
                using (Stream stream = response.GetResponseStream())
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        int count = 0;
                        do
                        {
                            count = stream.Read(buffer, 0, buffer.Length);
                            memoryStream.Write(buffer, 0, count);
                        } while (count != 0);
                        context.Response.ClearHeaders();
                        context.Response.ContentType = response.ContentType;
                        foreach (string HeaderKey in response.Headers.Keys)
                        {
                            context.Response.AddHeader(HeaderKey, response.Headers[HeaderKey]);
                        }
                        context.Response.StatusCode = (int)response.StatusCode;
                        context.Response.StatusDescription = response.StatusDescription;
                        context.Response.BinaryWrite(memoryStream.ToArray());
                    }
                }

            }
            catch (WebException code)
            {
                switch (code.Message)
                {
                    case "The operation has timed out":
                        break;
                    case "The remote server returned an error: (404) Not Found.":
                        context.Response.StatusCode = 404;
                        context.Response.Status = "404 File Not Found";
                        return;
                    default:
                        RetryCount++;
                        if (RetryCount > MaxRetryCount) return;
                        SearchPort();
                        DoRequest(context);
                        break;
                }
                context.Response.ClearContent();
                context.Response.Write("503 Gateway timeout");
                return;
            }
        }


        public void SearchPort()
        {
            if (Port.Length > 0) return;
            Port = "";
            for (int port = InitialPort; port < MaxPort; port++)
            {
                ports.Enqueue(port);
                threads.Add(new Thread(new ThreadStart(PortTest)));
            }
            threads.ForEach(new Action<Thread>(ThreadStart));

            while ((Port.Length == 0) && (TreadsIsLive()))
            {
                //ждем ответа от трэдов
            }
            //завершаем все трэды
            threads.ForEach(new Action<Thread>(ThreadAbort));
        }

        public void Dispose() { }

        public bool TreadsIsLive()
        {
            ThreadsLive = false;
            threads.ForEach(new Action<Thread>(ThreadTest));
            return ThreadsLive;
        }

        public void ThreadTest(Thread t)
        {
            ThreadsLive = ThreadsLive || t.IsAlive;
        }

        protected void ThreadStart(Thread t)
        {
            try
            {
                ThreadsLive = true;
                if (!t.IsAlive) t.Start();
            }
            catch { }
        }

        protected void ThreadAbort(Thread t)
        {
            t.Abort();
        }

        protected void PortTest()
        {
            int port;
            try
            {
                port = ports.Dequeue();
                HttpWebRequest myHttpWebRequest =
                      (HttpWebRequest)HttpWebRequest.Create(Protocol + Domain + ":" + port.ToString() + "/domain.txt");
                myHttpWebRequest.Timeout = 100;
                try
                {

                    HttpWebResponse myHttpWebResponse =
                        (HttpWebResponse)myHttpWebRequest.GetResponse();
                    using (Stream stream = myHttpWebResponse.GetResponseStream())
                    {
                        StreamReader msr = new StreamReader(stream);
                        string result = msr.ReadToEnd();
                        result = result.Replace("\r", "");
                        var domains = result.Split('\n');
                        if (domains.Contains<string>(ServerName))
                        {
                            Ports[ServerName] = port.ToString();
                            Port = port.ToString();
                            if (Secured) SecurePort = Port; else InsecurePort = Port;
                        }
                    }
                    return;
                }
                catch { }
                finally
                {
                }
            }
            catch { }
        }

        public bool VerifyCert(
    Object sender,
    X509Certificate certificate,
    X509Chain chain,
    SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
    }
}